/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com 

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SIMULATION_SCENE_H__
#define _SIMULATION_SCENE_H__

#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include "../sigslot/sigslot.h"

//Include library header
#include "../scene.h"
#include "simulation_entity.h"

namespace tri{
// Controls all object that is displayed to the screen.
class TSimulationScene : public tri::TScene,
	public sigslot::has_slots<sigslot::multi_threaded_local> {

public:
	TSimulationScene() :
		tri::TScene(),
		m_UpdateRecursion(40){
		initPhysicsWorld();
	}

	virtual ~TSimulationScene() {
		mPhysicsUpdateThread->terminate();
		mPhysicsUpdateThread->wait_on_exit();
		NewtonDestroyAllBodies (m_NewtonWorld);
		NewtonDestroy (m_NewtonWorld);
	}
	
	virtual void update(){
		tri::TScene::update();
		for (unsigned int i=0;i<m_UpdateRecursion;i++){
			NewtonUpdate (m_NewtonWorld, 0.002);
		}
	}
	
//	void addSimulationEntity(tri::TSimulationEntity* Entity,bool visualPhysics = false){
//		if(Entity->getGraphicsComponent()!=NULL){
//			if (!hasEntity(Entity->getGraphicsComponent())){
//				addEntity(Entity->getGraphicsComponent());
//				
//				
//			}else{
//				return;
//			}
//		}
//		
//		if(visualPhysics){
//			addEntity(Entity->getPhysicsModelComponent());
//		}
//		
//	}

	virtual void onResize(int w, int h) {
		getCamera()->setAspectRatio((float)w/h); // maintain a constant aspect-ratio
	}

	void setPhysicsUpdateSpeed(int s){
		m_UpdateRecursion = s;
	}
	
	void initPhysicsWorld() {
		// set the memory allocators
		NewtonSetMemorySystem (TAllocMemory, TFreeMemory);

		// create the Newton World
		m_NewtonWorld = NewtonCreate ();

		// use the standard x87 floating point model  
		NewtonSetPlatformArchitecture (m_NewtonWorld, 0);

		float size = 2000;
		// set a fix world size
		float minSize[3] = { -size, -size, -size };
		float maxSize[3] = { size, size, size };
		NewtonSetWorldSize (m_NewtonWorld, &minSize[0], &maxSize[0]);

		// configure the Newton world to use iterative solve mode 0
		// this is the most efficient but the less accurate mode
		NewtonSetSolverModel (m_NewtonWorld, 1);
		
		//create update thread
		mPhysicsUpdateThread = new tri::TPhysicsUpdateThread(m_NewtonWorld);
		//mPhysicsUpdateThread->start();
	}
	
	NewtonWorld* getPhysicsWorld(){
		return m_NewtonWorld;
	}
	
	void addSimulationEntity(TSimulationEntity* ent, bool owner=false){
		if (!containsSimulationEntity(ent)){
			if (ent->getParent()!=NULL){
				ent->getParent()->removeSimulationEntity(ent);
			}
			m_SimulationEntityList.insert(ent);
			ent->setParent(this, owner);
		}
	}
	
	bool containsSimulationEntity(TSimulationEntity* ent)
	{
		std::set<TSimulationEntity*>::iterator result = m_SimulationEntityList.find(ent);
		return result==m_SimulationEntityList.end() ? false : true;
	}
	
	void removeSimulationEntity(TSimulationEntity* ent){
		std::set<TSimulationEntity*>::iterator result = m_SimulationEntityList.find(ent);
		if (result != m_SimulationEntityList.end()){
			m_SimulationEntityList.erase(result);
			ent->freeParent();
			if (ent->isOwnedByParent()){
				delete ent;
			}
		}
	}
	
	void clearSimulationEntityList(){
		
	}

private:
	NewtonWorld* m_NewtonWorld;
	unsigned int m_UpdateRecursion; 
	tri::TPhysicsUpdateThread* mPhysicsUpdateThread;
	std::set<TSimulationEntity*> m_SimulationEntityList;
};

}

#endif
